package com.generate.service;

import com.generate.service.model.ColumnModel;
import com.generate.util.CommonUtil;
import com.generate.util.FileUtil;
import com.generate.util.dict.*;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.*;

/**
 * ${desc}
 * <p>
 * User: purple Date: 2017/11/14 Version: 1.0
 * </p>
 */
@Slf4j
@Service
public class CodeGenerateService {
    @Autowired
    private SqlSessionTemplate sqlSession;

    private final String packageName = "com.purple";
    private final String author = "purple";
    private String templatePath = "";
    private String generatePath = "";

    {
        templatePath = CommonUtil.getPath().concat(GenerateDict.TEMPLATE_PATH);
        generatePath = CommonUtil.getPath().concat(GenerateDict.GENERATE_PATH);

    }

    public void start() {
        deleteDir();
        List<Map<String, ColumnModel>> tableList = new ArrayList<>();
        Set<String> columnSet = new HashSet<>();
        Connection connection;
        try {
            connection = sqlSession.getConfiguration().getEnvironment().getDataSource().getConnection();
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet rs = metaData.getColumns(connection.getCatalog(), null, "%", "%");
            while (rs.next()) {
                Map<String, ColumnModel> tableMap = new HashMap<>();
                ColumnModel columnModel = new ColumnModel();
                columnModel.setColumnName(rs.getString(GenerateDict.COLUMN_NAME));
                columnModel.setDataType(rs.getString(GenerateDict.DATA_TYPE));
                columnModel.setRemarks(rs.getString(GenerateDict.REMARKS));
                tableMap.put(rs.getString(GenerateDict.TABLE_NAME), columnModel);
                columnSet.add(rs.getString(GenerateDict.TABLE_NAME));
                tableList.add(tableMap);
            }
            List<Map<String, Set<ColumnModel>>> newTableList = new ArrayList<>();
            for (String columnSetItem : columnSet) {
                Map<String, Set<ColumnModel>> newTableMap = new HashMap<>();
                Set<ColumnModel> newColumnSet = new HashSet<>();
                for (Map<String, ColumnModel> tableListItem : tableList) {
                    if (tableListItem.keySet().iterator().next().equals(columnSetItem)) {
                        newColumnSet.add(tableListItem.get(columnSetItem));
                    }
                }
                newTableMap.put(columnSetItem, newColumnSet);
                newTableList.add(newTableMap);
            }
            generateBiz(newTableList);
        } catch (Exception e) {
            log.error("获取数据列异常:", e);
        }
    }

    private void generateBiz(List<Map<String, Set<ColumnModel>>> newTableList) {
        Configuration cfg = new Configuration();
        Writer out = null;
        try {
            DateTimeFormatter format = DateTimeFormat.forPattern(CommonDict.YYYY_MM_DD_BLANK_HH_MM_SS);
            String date = new DateTime().toString(format);
            cfg.setDirectoryForTemplateLoading(new File(templatePath));  //模板父路径
            cfg.setObjectWrapper(new DefaultObjectWrapper());
            Template dalResourceTemp = cfg.getTemplate(GenerateDict.DAL_RESOURCETEMP_TEMPLATE);
            Template dalModelTemp = cfg.getTemplate(GenerateDict.DAL_MODEL_TEMPLATE);
            Template dalMapperTemp = cfg.getTemplate(GenerateDict.DAL_MAPPER_TEMPLATE);
            Template managerTemp = cfg.getTemplate(GenerateDict.MANAGER_TEMPLATE);
            Template managerImplTemp = cfg.getTemplate(GenerateDict.MANAGER_IMPL_TEMPLATE);
            Template bizTemp = cfg.getTemplate(GenerateDict.BIZ_TEMPLATE);
            Template bizImplTemp = cfg.getTemplate(GenerateDict.BIZ_IMPL_TEMPLATE);
            Template serviceTemp = cfg.getTemplate(GenerateDict.SERVICE_TEMPLATE);
            Template serviceImplTemp = cfg.getTemplate(GenerateDict.SERVICE_IMPL_TEMPLATE);
            Template controllerTemp = cfg.getTemplate(GenerateDict.CONTROLLER_TEMPLATE);
            String classPath = getClassPath(packageName);
            String resourcePath = getResourcePath(packageName);
            FileUtil.createDirectory(resourcePath.concat(PathDict.DAL_MAPPER));
            FileUtil.createDirectory(classPath.concat(PathDict.DAL_RESOURCES));
            FileUtil.createDirectory(classPath.concat(PathDict.DAL_MODEL));
            FileUtil.createDirectory(classPath.concat(PathDict.MANAGER));
            FileUtil.createDirectory(classPath.concat(PathDict.MANAGER_IMPL));
            FileUtil.createDirectory(classPath.concat(PathDict.BIZ));
            FileUtil.createDirectory(classPath.concat(PathDict.BIZ_IMPL));
            FileUtil.createDirectory(classPath.concat(PathDict.SERVICE));
            FileUtil.createDirectory(classPath.concat(PathDict.SERVICE_IMPL));
            FileUtil.createDirectory(classPath.concat(PathDict.WEB));
            for (Map<String, Set<ColumnModel>> newTableListItem : newTableList) {
                for (String keyItem : newTableListItem.keySet()) {
                    String dtoStr = CommonUtil.underline2Camel(keyItem, Boolean.FALSE);
                    String className = dtoStr.substring(NumberDict.ONE, dtoStr.length());
                    Map<String, Object> root = new HashMap<>();  //注意必须有一个根结点 data-model
                    root.put(TemplateDict.PACKAGE_PATH, packageName);
                    root.put(TemplateDict.CLASS_NAME, className);
                    root.put(TemplateDict.DTO_NAME, dtoStr);
                    root.put(TemplateDict.AUTHOR, author);
                    root.put(TemplateDict.DATE, date);
                    List<Map<String, String>> columnList = new ArrayList<>();
                    for (ColumnModel columnItem : newTableListItem.get(keyItem)) {
                        String propertyName = CommonUtil.underline2Camel(columnItem.getColumnName(), Boolean.TRUE);
                        Map<String, String> columnMap = new HashMap<>();
                        columnMap.put(TemplateDict.COLUMN_NAME, columnItem.getColumnName());
                        columnMap.put(TemplateDict.PROPERTY_NAME, propertyName);
                        columnMap.put(TemplateDict.REMARKS, columnItem.getRemarks());
                        columnMap.put(TemplateDict.TYPE, getType(columnItem.getDataType()));
                        columnList.add(columnMap);
                    }
                    root.put(TemplateDict.COLUMN, columnList);
                    root.put(TemplateDict.TABLE_NAME, keyItem);
                    File dtoModelFile = new File(classPath.concat(PathDict.DAL_MODEL), className.concat(GenerateDict.DAL_MODEL_NAME));
                    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dtoModelFile), CommonDict.UTF_8));
                    dalModelTemp.process(root, out);
                    out.flush();

                    File dtoResourceFile = new File(resourcePath.concat(PathDict.DAL_RESOURCES), className.concat(GenerateDict.DAL_RESOURCES_NAME));
                    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dtoResourceFile), CommonDict.UTF_8));
                    dalResourceTemp.process(root, out);
                    out.flush();

                    root.remove(TemplateDict.COLUMN);
                    root.remove(TemplateDict.TABLE_NAME);
                    File dtoManagerFile = new File(classPath.concat(PathDict.DAL_MAPPER), className.concat(GenerateDict.DAL_MAPPER_NAME));
                    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dtoManagerFile), CommonDict.UTF_8));
                    dalMapperTemp.process(root, out);
                    out.flush();

                    File managerFile = new File(classPath.concat(PathDict.MANAGER), className.concat(GenerateDict.MANAGER_NAME));
                    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(managerFile), CommonDict.UTF_8));
                    managerTemp.process(root, out);
                    out.flush();

                    File managerImplFile = new File(classPath.concat(PathDict.MANAGER_IMPL), className.concat(GenerateDict.MANAGER_IMPL_NAME));
                    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(managerImplFile), CommonDict.UTF_8));
                    managerImplTemp.process(root, out);
                    out.flush();

                    File bizFile = new File(classPath.concat(PathDict.BIZ), className.concat(GenerateDict.BIZ_NAME));
                    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(bizFile), CommonDict.UTF_8));
                    bizTemp.process(root, out);
                    out.flush();

                    File bizImplFile = new File(classPath.concat(PathDict.BIZ_IMPL), className.concat(GenerateDict.BIZ_IMPL_NAME));
                    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(bizImplFile), CommonDict.UTF_8));
                    bizImplTemp.process(root, out);
                    out.flush();

                    File serviceFile = new File(classPath.concat(PathDict.SERVICE), className.concat(GenerateDict.SERVICE_NAME));
                    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(serviceFile), CommonDict.UTF_8));
                    serviceTemp.process(root, out);
                    out.flush();

                    File serviceImplFile = new File(classPath.concat(PathDict.SERVICE_IMPL), className.concat(GenerateDict.SERVICE_IMPL_NAME));
                    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(serviceImplFile), CommonDict.UTF_8));
                    serviceImplTemp.process(root, out);
                    out.flush();

                    File controllerFile = new File(classPath.concat(PathDict.WEB), className.concat(GenerateDict.CONTROLLER_NAME));
                    out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(controllerFile), CommonDict.UTF_8));
                    controllerTemp.process(root, out);
                    out.flush();
                }
            }
        } catch (Exception e) {
            log.error("生成biz层异常:", e);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                log.error("关闭io流异常:", e);
            }
        }
    }
    private String getType(String dateType){
        String type = "";
        switch (dateType){
            case "INT":
            case "BIGINT":
            case "TINYINT":
            case "BIT":
            case "SMALLINT":
                type = "Integer";
                break;
            case "TIMESTAMP":
            case "DATETIME":
                type = "Date";
                break;
            case "DECIMAL":
                type = "BigDecimal";
                break;
            case "TEXT":
            case "VARCHAR":
                type = "String";
                break;

        }
        return type;
    }
    private void deleteDir() {
        File tmp_file = new File(generatePath);
        FileUtil.recurDelete(tmp_file);
        FileUtil.createDirectory(generatePath);
    }

    private String getResourcePath(String packageName) {
        packageName = packageName.replace(".", "\\/");
        return generatePath.concat(GenerateDict.RESOURCES_PATH).concat(packageName);
    }
    private String getClassPath(String packageName) {
        packageName = packageName.replace(".", "\\/");
        return generatePath.concat(GenerateDict.CLASS_PATH).concat(packageName);
    }
}
